﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Weka
{

    internal class FieldDeserialiserProviderInt32N<T> : FieldDeserialiserProvider<T, Int32?>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserInt32N<T>() { Writer = Writer }; }
    }
    internal class FieldDeserialiserInt32N<T> : FieldDeserialiser<T, Int32?>
    {
        public override void ReadWriteValue(T value, StreamReada sr)
        {
            Int32? v;
            if (Header == 0) v = null;
            else if (Header == 1) v = sr.ReadByte();
            else if (Header == 2) v = sr.ReadUInt16();
            else v = sr.ReadInt32();

            Writer(value, v);
        }
    }

    internal class FieldDeserialiserProviderInt32<T> : FieldDeserialiserProvider<T, Int32>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser()
        {
            return new FieldDeserialiserInt32<T>() { Writer = Writer };
        }
    }

    internal class FieldDeserialiserInt32<T> : FieldDeserialiser<T, Int32>
    {
        public override void ReadWriteValue(T value, StreamReada sr)
        {
            if (Header == 0) throw new Exception("Invalid data");//TODO

            Int32 v;
            if (Header == 1) v = sr.ReadByte();
            else if (Header == 2) v = sr.ReadUInt16();
            else v = sr.ReadInt32();

            Writer(value, v);
        }
    }


    internal class FieldDeserialiserProviderInt16N<T> : FieldDeserialiserProvider<T, Int16?>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserInt16N<T>() { Writer = Writer }; }
    }
    internal class FieldDeserialiserInt16N<T> : FieldDeserialiser<T, Int16?>
    {
        public override void ReadHeader(StreamReada streamReader)
        {
            int h = streamReader.HeaderReadBit();
            if (h == 1)
                h |= (streamReader.HeaderReadBit() << 1);

            Header = (byte)h;
        }
        public override void ReadWriteValue(T value, StreamReada sr)
        {
            Int16? v;
            if (Header == 0) v = sr.ReadInt16();
            else if (Header == 1) v = null;
            else v = sr.ReadByte();

            Writer(value, v);
        }
    }

    internal class FieldDeserialiserProviderInt16<T> : FieldDeserialiserProvider<T, Int16>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser()
        {
            return new FieldDeserialiserInt16<T>() { Writer = Writer };
        }
    }

    internal class FieldDeserialiserInt16<T> : FieldDeserialiser<T, Int16>
    {
        public override void ReadHeader(StreamReada streamReader)
        {
            int h = streamReader.HeaderReadBit();
            if (h == 1)
                h |= (streamReader.HeaderReadBit() << 1);

            Header = (byte)h;
        }

        public override void ReadWriteValue(T value, StreamReada sr)
        {
            if (Header == 1) throw new Exception("Invalid data");//TODO

            Int16 v;
            if (Header == 0) v = sr.ReadInt16();
            else v = sr.ReadByte();

            Writer(value, v);
        }
    }

    internal abstract class FieldDeserialiserDecimalBase<T, TD> : FieldDeserialiser<T, TD>
    {
        public override void ReadHeader(StreamReada streamReader)
        {
            int h = streamReader.HeaderReadBit() << 5;//Test for is null.
            if (h != 0)
            {
                h |= streamReader.HeaderReadBit() << 4;//Test for is negative.
                h |= streamReader.HeaderReadBits4();//Add in byte count.
            }
            Header = (byte)h;
        }

        public decimal? ReadValue(StreamReada sr)
        {
            if (Header == 0)
            {
                return null;
            }
            else
            {
                bool isNegative = (((Header >> 4) & 0x01) == 1);
                int byteCount = (Header & 0x0F);

                byte scale = sr.ReadByte();

                int lo = ReadInt(sr, byteCount);
                int mid = ReadInt(sr, byteCount - 4);
                int hi = ReadInt(sr, byteCount - 8);

                decimal v = new decimal(lo, mid, hi, isNegative, scale);

                return v;
            }
        }

        private int ReadInt(StreamReada sr, int byteCount)
        {
            if (byteCount <= 0) return 0;

            if (byteCount > 4) byteCount = 4;

            int v = 0;

            for (int i = 0; i < byteCount; i++)
            {
                byte b = sr.ReadByte();
                v |= (b << (i << 3));
            }

            return v;
        }
    }



    internal class FieldDeserialiserDecimal<T> : FieldDeserialiserDecimalBase<T, decimal>
    {
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            Writer(value, ReadValue(streamReader).Value);
        }
    }

    internal class FieldDeserialiserDecimalN<T> : FieldDeserialiserDecimalBase<T, decimal?>
    {
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            Writer(value, ReadValue(streamReader));
        }
    }



    internal class FieldDeserialiserProviderBoolean<T> : FieldDeserialiserProvider<T, bool>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserBoolean<T>() { Writer = Writer }; }
    }
    internal class FieldDeserialiserBoolean<T> : FieldDeserialiser<T, bool>
    {
        public override void ReadHeader(StreamReada streamReader)
        {
            Header = streamReader.HeaderReadBit();
        }
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            Writer(value, (Header == 1));
        }
    }


    internal class FieldDeserialiserProviderBooleanN<T> : FieldDeserialiserProvider<T, bool?>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserBooleanN<T>() { Writer = Writer }; }
    }
    internal class FieldDeserialiserBooleanN<T> : FieldDeserialiser<T, bool?>
    {
        public override void ReadHeader(StreamReada streamReader)
        {
            if (streamReader.HeaderReadBit() == 1)
                Header = (byte)(streamReader.HeaderReadBit() + 1);
            else
                Header = 0;
        }
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            bool? v;
            if (Header == 0) v = null;
            else v = (Header == 2);

            Writer(value, v);
        }
    }



    internal class FieldDeserialiserString<T> : FieldDeserialiser<T, string>
    {
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            string v;
            if (Header == 0)
            {
                v = null;
            }
            else if (Header == 1)
            {
                v = string.Empty;
            }
            else
            {
                int l = (Header == 2) ? (streamReader.ReadByte() + 1) : streamReader.ReadInt32();
                v = streamReader.ReadStringRaw(l);
            }
            Writer(value, v);
        }
    }

    internal class FieldDeserialiserProviderString<T> : FieldDeserialiserProvider<T, string>
    {
        public Func<T, string> Reader { get; set; }
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserString<T>() { Writer = Writer }; }
    }




    internal class FieldDeserialiserProviderDateTime<T> : FieldDeserialiserProvider<T, DateTime>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserDateTime<T>() { Writer = Writer }; }
    }
    internal sealed class FieldDeserialiserDateTime<T> : FieldDeserialiserDateTimeBase<T, DateTime>
    {
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            Writer(value, ReadDate(streamReader).Value);
        }
    }





    internal class FieldDeserialiserProviderDateTimeN<T> : FieldDeserialiserProvider<T, DateTime?>
    {
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserDateTimeN<T>() { Writer = Writer }; }
    }

    internal sealed class FieldDeserialiserDateTimeN<T> : FieldDeserialiserDateTimeBase<T, DateTime?>
    {
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            Writer(value, ReadDate(streamReader));
        }
    }

    internal abstract class FieldDeserialiserDateTimeBase<T, T2> : FieldDeserialiser<T, T2>
    {
        private static readonly DateTime _minDate = new DateTime(1950, 1, 1);
        private static readonly DateTime _maxDate = _minDate.AddDays(UInt16.MaxValue / 2);

        DateTimeKind? _dateTimeKind;
        public override void ReadHeader(StreamReada sr)
        {
            byte h = sr.HeaderReadBits2();
            if (h == 0)
            {
                _dateTimeKind = null;
                return;
            }

            if (h == 1) _dateTimeKind = DateTimeKind.Local;
            else if (h == 2) _dateTimeKind = DateTimeKind.Utc;
            else _dateTimeKind = DateTimeKind.Unspecified;

            h = (byte)(sr.HeaderReadBit() << 1);
            if (h == 0) h = sr.HeaderReadBit();
            Header = h;
        }
        protected DateTime? ReadDate(StreamReada sr)
        {
            //            const byte DateFull = 2;
            const byte DateDayTime = 1;
            const byte DateDay = 0;

            if (!_dateTimeKind.HasValue) return null;

            long v;

            if (Header == DateDay)
            {
                v = _minDate.AddDays(sr.ReadUInt16()).Ticks;
            }
            else if (Header == DateDayTime)
            {
                int v2 = sr.ReadUInt16();
                DateTime dt = _minDate.AddDays(v2 >> 1);
                if ((v2 & 0x01) == 0x01) dt = dt.AddHours(12);
                v = dt.AddMinutes(sr.ReadByte() * 5).Ticks;
            }
            else
            {
                v = sr.ReadInt64();
            }

            return new DateTime(v, _dateTimeKind.Value);
        }

    }




    internal class FieldDeserialiserFuncStream<T> : FieldDeserialiser<T, Func<Stream>>
    {
        public override void ReadHeader(StreamReada streamReader) { }
        public override void ReadWriteValue(T value, StreamReada streamReader)
        {
            byte[] data = null;
            byte h = streamReader.ReadByte();
            if (h > 0)
            {
                int length = (h == 1) ? streamReader.ReadInt32() : h - 2;
                data = new byte[length];

                streamReader.Populate(data);
            }

            Func<Stream> sc = () => new MemoryStream(data, false);
            Writer(value, sc);
        }
    }

    internal class FieldDeserialiserProviderFuncStream<T> : FieldDeserialiserProvider<T, string>
    {
        public Func<T, Func<Stream>> Reader { get; set; }
        public override FieldDeserialiser<T> CreateFieldDeserialiser() { return new FieldDeserialiserString<T>() { Writer = Writer }; }
    }



}
